package com.zhongke.cotrun.common.util;


import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * <p>作者 yhl
 * <p>日期 2018-06-25 16:14
 * <p>说明 ...
 */
public abstract class AbstractGuavaCache<K, V> extends ReentrantLock implements GuavaCache<K, V> {
    private static final Logger logger = LoggerFactory.getLogger(AbstractGuavaCache.class);

    protected LoadingCache<K, V> GUAVA_CACHE = null;

    public boolean initCacheIfNecessary() {
        if (GUAVA_CACHE != null) {
            return true;
        }

        // 只初始化一次
        lock();
        try {
            if (GUAVA_CACHE != null) {
                return true;
            }
            GUAVA_CACHE = CacheBuilder.newBuilder()
                    .maximumSize(initMaximumSize())
                    .expireAfterAccess(initTimeOut(), initTimeUnit())
                    .build(new CacheLoader<K, V>() {
                               @Override
                               public V load(K key) throws Exception {
                                   return loadData(key);
                               }
                           }
                    );
        } catch (Exception e) {
            logger.error("初始化缓存失败，error msg = {}", e.getMessage(), e);
            return false;
        } finally {
            unlock();
        }
        return true;
    }

    @Override
    public TimeUnit initTimeUnit() {
        return TimeUnit.SECONDS;
    }

    @Override
    public long initTimeOut() {
        return 60;
    }

    @Override
    public V getFromCache(K k) {
        if (k == null) {
            return null;
        }
        initCacheIfNecessary();

        try {
            return GUAVA_CACHE.getUnchecked(k);
        } catch (Exception e) {
            if (e instanceof CacheLoader.InvalidCacheLoadException) {
                logger.info("从本地缓存获取数据为空！k = {}", k);
            } else {
                logger.error("从本地缓存获取异常。k = {}, msg = {}", k, e.getMessage(), e);
            }
            return null;
        }
    }

    @Override
    public Collection<V> getAllForList(Set<K> ks) {
        Map<K, V> result = getAll(ks);
        if (result == null || result.size() == 0) {
            return Lists.newArrayList();
        }
        return result.values();
    }

    @Override
    public Map<K, V> getAll(Set<K> ks) {
        Map<K, V> result = Maps.newHashMap();
        if (ks == null || ks.size() == 0) {
            return result;
        }
        for (K k : ks) {
            if (k == null) {
                continue;
            }
            try {
                V v = this.getUnchecked(k);
                if (v == null) {
                    continue;
                }
                result.put(k, v);
            } catch (Exception e) {
                logger.warn("获取数据失败，key = {}, error msg = {}", k, e.getMessage(), e);
            }

        }
        return result;
    }

    @Override
    public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
        this.initCacheIfNecessary();

        return GUAVA_CACHE.getAll(checkNotNull(keys));
    }

    @Override
    public void put(K key, V value) {
        this.initCacheIfNecessary();

        GUAVA_CACHE.put(checkNotNull(key), value);
    }

    @Override
    public long size() {
        initCacheIfNecessary();

        return GUAVA_CACHE.size();
    }

    @Override
    public V get(K key) throws ExecutionException {
        this.initCacheIfNecessary();

        return GUAVA_CACHE.get(checkNotNull(key));
    }

    @Override
    public V getUnchecked(K key) {
        this.initCacheIfNecessary();

        return GUAVA_CACHE.getUnchecked(checkNotNull(key));
    }

    @Override
    public void cleanUp() {
        GUAVA_CACHE.cleanUp();
    }

    @Override
    public void refresh(K key) {
        GUAVA_CACHE.refresh(key);
    }
}
